package com.feiyue.learning.distributionlock;

import org.I0Itec.zkclient.IZkDataListener;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 同名时序节点分布式锁
 * @author  feiyue
 * @date  2020/5/3
 */
public class ZkDistributionLock2 extends ZkAbstractLock {
    private CountDownLatch countDownLatch;
    private String currentPath;
    private String previousPath;

    public ZkDistributionLock2() {
        if (!zkClient.exists(LOCK_2)) {
            zkClient.createPersistent(LOCK_2);
        }
    }

    @Override
    public boolean tryLock() {
        if (currentPath == null || currentPath.length() <= 0) {
            currentPath = zkClient.createEphemeralSequential(LOCK_2 + "/", "lock");
        }

        // 获取所有临时顺序子节点，并大小排序
        List<String> children = zkClient.getChildren(LOCK_2);
        Collections.sort(children);
        if (currentPath.equals(LOCK_2.concat("/").concat(children.get(0)))) {
            return true;
        }

        // currentPath 为 /lock2/xxx, 所以需要去掉前面 7 个字符
        int index = Collections.binarySearch(children, currentPath.substring(LOCK_2.length() + 2));

        // 计算上一个节点的路径
        previousPath = LOCK_2.concat("/").concat(children.get(index-1));
        return false;
    }

    @Override
    public void waitLock() {

        // 监听上一个序号节点的删除
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        zkClient.subscribeDataChanges(previousPath, listener);

        if (zkClient.exists(previousPath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        zkClient.unsubscribeDataChanges(previousPath, listener);
    }

    @Override
    public void unlock() {
        if (zkClient != null) {
            zkClient.delete(currentPath);
            zkClient.close();
        }
    }
}
